using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using FireStarter.Base;
using FireStarter.CodeGeneration.Generator;
using FireStarter.CodeGeneration.Parser;
using FireStarterModeller.BO;
using Habanero.Util;

namespace FireStarter.CodeGeneration
{
    public static class Utilities
    {
        /// <summary>
        /// Indicates whether the start and end of the string contains quotes
        /// </summary>
        public static bool StringIsWrappedInQuotes(string stringPart)
        {
            if (String.IsNullOrEmpty(stringPart)) return false;
            if (stringPart.Length < 2) return false;
            return stringPart[0] == '"' && stringPart[stringPart.Length - 1] == '"';
        }

        /// <summary>
        /// Searches for the last occurrence of a newline and indicates whether the
        /// text after this occurrence is only whitespace characters, tabs or an empty string.
        /// Returns false if there is no newline.
        /// </summary>
        public static bool StringHasOnlyWhitespaceSinceLastNewline(string result)
        {
            if (result.Contains(Environment.NewLine))
            {
                do // keeps eliminating everything left of and including the next newline until there are no newlines left
                {
                    result = StringUtilities.GetRightSection(result, Environment.NewLine);
                } while (result.Contains(Environment.NewLine));
                result = result.Replace(" ", "");
                result = result.Replace("\t", "");
                return result.Length == 0;
            }
            return false;
        }

        public static string GetRelativePath(string absolutePath, string relativeTo)
        {
            string rootPath = relativeTo;
            string directorySeperator = Path.DirectorySeparatorChar.ToString();
            if (!rootPath.EndsWith(directorySeperator))
            {
                rootPath = rootPath + directorySeperator;
            }
            //rootPath = GetRootedPath(basePath, rootPath);
            Uri outputDir = new Uri(rootPath);
            Uri fileDir = new Uri(absolutePath);
            return outputDir.MakeRelativeUri(fileDir).ToString().Replace("/", "\\");
        }

        //public static string GetRootedPath(string basePath, string relativePath)
        //{
            
        //}

        public static string CreateFile(string filePath, string fileContents)
        {
            return CreateFile(Path.GetDirectoryName(filePath), Path.GetFileName(filePath), fileContents, null);
        }

        public static string CreateFile(string filePath, string fileContents, bool overwrite)
        {
            return CreateFile(Path.GetDirectoryName(filePath), Path.GetFileName(filePath), fileContents, overwrite);
        }

        public static string CreateFile(string folderPath, string filePath, string fileContents)
        {
            return CreateFile(folderPath, filePath, fileContents, null);
        }

        public static string CreateFile(string folderPath, string filePath, string fileContents, Encoding encoding)
        {
            return CreateFile(folderPath, filePath, fileContents, false, encoding);
        }

        public static string CreateFile(string folderPath, string filePath, string fileContents, bool overwrite)
        {
            return CreateFile(folderPath, filePath, fileContents, overwrite, null);
        }

        public static string CreateFile(string folderPath, string filePath, string fileContents, bool overwrite, Encoding encoding)
        {
            string destFile = Path.Combine(folderPath, filePath);
            string path = Path.GetDirectoryName(destFile);
            if (!File.Exists(destFile) || overwrite)
            {
                if (!Directory.Exists(path))
                {
                    Directory.CreateDirectory(path);
                }
                if (encoding != null)
                {
                    File.WriteAllText(destFile, fileContents, encoding);
                }
                else
                {
                    File.WriteAllText(destFile, fileContents);
                }
            }
            return destFile;
        }

        /// <summary>
        /// DEPRECATED - the logic has been moved into the generated TestUtil class
        ///    in order to make the TestUtil class write-once but still keep deletion dynamic
        /// 
        /// This utility ensures that deletion from tables is done in the correct
        /// order, so that foreign keys are not violated.  It uses single vs multiple
        /// relationships to determine which classes are children and which are parents,
        /// so that a parent is never deleted before its children.
        /// At a more advanced level, this could possibly consider delete actions as well.
        /// </summary>
        /// <param name="modelClasses">The collection to reorder</param>
        /// <returns>The reordered collection</returns>
        public static IList<IModelElement> OrderModelClassesForDeletion(IList<IModelClass> modelClasses)
        {
            bool deleteOrderCorrect;
            do
            {
                deleteOrderCorrect = true;
                for (int position = 0; position < modelClasses.Count; position++)
                {
                    IModelClass dmClass = modelClasses[position];
                    for (int parentPosition = 0; parentPosition < position; parentPosition++)
                    {
                        IModelClass potentialParent = modelClasses[parentPosition];
                        if (ClassMustBeDeletedBeforeParent(dmClass, potentialParent))
                        {
                            modelClasses.Remove(dmClass);
                            modelClasses.Insert(parentPosition, dmClass);
                            deleteOrderCorrect = false;
                            break;
                        }
                    }
                    if (!deleteOrderCorrect) break;
                }
            } while (!deleteOrderCorrect);

            IList<IModelElement> modelClassesToReturn = new List<IModelElement>();
            foreach (IModelClass dmClass in modelClasses)
            {
                modelClassesToReturn.Add(dmClass);
            }

            return modelClassesToReturn;
        }

        private static bool ClassMustBeDeletedBeforeParent(IModelClass childClass, IModelClass potentialParent)
        {
            foreach (IModelRelationship relationship in potentialParent.Relationships)
            {
                if (relationship.RelatedClass + relationship.RelatedAssembly == childClass.ClassName + childClass.AssemblyName &&
                        relationship.IsMultiple)
                {
                    return true;
                }
            }
            return false;
        }
        /// <summary>
        /// Verifies that the Model Element is the correct type for this delegate
        /// </summary>
        /// <param name="delegateName">The delegate being verified</param>
        /// <param name="modelElement">The model element being checked</param>
        /// <param name="typeRequired">The type required for the delegate</param>
        public static void CheckDelegateContext(string delegateName, IModelElement modelElement, Type typeRequired)
        {
            if (!typeRequired.IsInstanceOfType(modelElement))
            {
                throw new GenerationException(
                    String.Format("You cannot call '{0}' when the context is not a type of {1}",
                                  delegateName, typeRequired.Name));
            }
        }

        public static void CheckPropertyHasParent(IModelProperty modelProperty)
        {
            if (modelProperty.Parent == null || !(modelProperty.Parent is IModelClass))
            {
                throw new GenerationException(String.Format(
                                                  "A model property called '{0}' does not have a parent, or the parent is not a class",
                                                  modelProperty.PropertyName));
            }
        }
    }
}
